#ifndef __BUFFPOOL_CPP__
#define __BUFFPOOL_CPP__

#include "glib.h"
#include <iostream>
#include <typeinfo>
#include <deque>
#include <common/log.h>
#include "image-buffpool.h"

using namespace std;

template <typename BuffType>
class BuffPool{
    public:
        BuffType* get_buff(void);
        void release_buff(BuffType* buff);
        uint32_t get_buffs_num(void);
        uint32_t get_buffs_num_max(void);
        uint32_t get_buffs_size(void);

        BuffPool(uint32_t objects_num,
                 uint32_t objects_num_max,
                 uint32_t objects_item_num = 1,
                 bool extention_able = true);
        ~BuffPool();

    private:
        GAsyncQueue*        work_buffer_queue;          //工作的buffer
        GAsyncQueue*        all_buffer_queue;           //记录所有new出来的buffer
        uint32_t 	        buffs_num{0};               //当前创建的buff总数
	    uint32_t 		    buffs_num_max{0};           //当前场景可能用到的buff的最大上限
        uint32_t            buffs_size{0};              //单个buff的size
        uint32_t            buffs_item_num{0};          //单个buff中item的数量,用于连续内存块申请
        bool                buffs_extension{true};      //控制buffpool是否支持扩展增大
};

template<typename BuffType>
BuffPool<BuffType>::BuffPool(uint32_t objects_num,
                             uint32_t objects_num_max,
                             uint32_t objects_item_num,
                             bool extention_able):
                             buffs_num(objects_num),
				             buffs_num_max(objects_num_max),
                             buffs_size(sizeof(BuffType) * objects_item_num),
                             buffs_item_num(objects_item_num),
                             buffs_extension(extention_able)
{
    if(buffs_num > buffs_num_max){
		buffs_num = buffs_num_max;
		compression_status("buff[%s] buffs_num exceed the limit buffs_num_max, error!!!", typeid(BuffType).name());
	}

    work_buffer_queue = g_async_queue_new();
    all_buffer_queue = g_async_queue_new();

    for (uint32_t i = 0; i < buffs_num; i++){
        BuffType* item = new BuffType[buffs_item_num];
        g_async_queue_push(work_buffer_queue, item);
        g_async_queue_push(all_buffer_queue, item);
    }

    compression_status("BuffPool of [%s] create! "
               "buffer_num[%u] "
               "buffs_num_max[%u] "
               "buffs_item_num[%u] "
               "extention_able[%d]",
               typeid(BuffType).name(),
               buffs_num,
               buffs_num_max,
               buffs_item_num,
               buffs_extension);
}

template<typename BuffType>
BuffPool<BuffType>::~BuffPool(){
    if (g_async_queue_length(all_buffer_queue) != buffs_num)
        compression_status("buffer [%s] leak!", typeid(BuffType).name());

    BuffType* item = NULL;
    while((item = (BuffType*)g_async_queue_try_pop(all_buffer_queue))){
        delete []item;
        item = NULL;
    }
    g_async_queue_unref(all_buffer_queue);  all_buffer_queue = NULL;
    g_async_queue_unref(work_buffer_queue);   work_buffer_queue = NULL;
}

template<typename BuffType>
BuffType* BuffPool<BuffType>::get_buff(){
    // compression_status("get buffer [%s] buffpool info: "
    //                    "buffs_num[%u] "
    //                    "buffs_num_max[%u] "
    //                    "work_buffer_queue[%d] "
    //                    "all_buffer_queue[%d]!",
    //                    typeid(BuffType).name(),
    //                    buffs_num,
    //                    buffs_num_max,
    //                    g_async_queue_length(work_buffer_queue),
    //                    g_async_queue_length(all_buffer_queue));
    BuffType* item = (BuffType*)g_async_queue_try_pop(work_buffer_queue);
    if(!item) {
        if (buffs_extension) {
            if(buffs_num >= buffs_num_max) {
		        compression_status("buff[%s] buffs_num exceed the limit buffs_num_max, return NULL!!!", typeid(BuffType).name());
                return NULL;
	        }
            item = new BuffType[buffs_item_num];
            g_async_queue_push(all_buffer_queue, item);
            buffs_num++;
        } else {
            compression_status("get buffer [%s] failed! extension_able[%d]!!!", typeid(BuffType).name(), buffs_extension);
            return NULL;
        }
    }

    if(!item){
        compression_status("get buffer [%s] failed!", typeid(BuffType).name());
        return NULL;
    }
    // compression_status("get buffer [%s] succeed!", typeid(BuffType).name());
    return item;
}

template<typename BuffType>
void BuffPool<BuffType>::release_buff(BuffType* item){
    g_async_queue_push(work_buffer_queue, item);
    // compression_status("release buffer [%s] succeed!", typeid(BuffType).name());
}

template<class BuffType>
uint32_t BuffPool<BuffType>::get_buffs_num(){
    return buffs_num;
}

template<class BuffType>
uint32_t BuffPool<BuffType>::get_buffs_num_max(){
    return buffs_num_max;
}

template<class BuffType>
uint32_t BuffPool<BuffType>::get_buffs_size(){
    return buffs_size;
}









//C interfaces

struct BuffPoolWapper {
    BuffPool<uint8_t>*       buff_pool{NULL};
    uint32_t 		         image_id{0};
};
BuffPoolWapper_t* buff_pool_create(uint32_t objects_num,
                                   uint32_t objects_num_max,
                                   uint32_t objects_item_num,
                                   bool extention_able)
{
    BuffPoolWapper_t* buffpoolWapper = (BuffPoolWapper_t*)malloc(sizeof(BuffPoolWapper_t));
    if (!buffpoolWapper) {
        compression_status("create buffpoolWapper failed!");
        return NULL;
    }
    buffpoolWapper->image_id = 0;
    buffpoolWapper->buff_pool = new BuffPool<uint8_t>(objects_num, objects_num_max, objects_item_num, extention_able);
    if (!buffpoolWapper->buff_pool) {
        compression_status("create buff_pool failed!");
        return NULL;
    }
    return buffpoolWapper;
}
bool buff_pool_destroy(BuffPoolWapper_t* buffpoolWapper){
    if (buffpoolWapper == NULL) {
        compression_status("destory NULL buffpoolWapper");
        return false;
    }
    delete buffpoolWapper->buff_pool; buffpoolWapper->buff_pool = NULL;
    free(buffpoolWapper);
    compression_status("destory buffpoolWapper");
    return true;
}
uint8_t* buff_pool_get_buff(BuffPoolWapper_t* buffpoolWapper){
    uint8_t* buff_item = (uint8_t*)buffpoolWapper->buff_pool->get_buff();
    // compression_status("buff_pool_get_buff buff[%p].", buff_item);
    return buff_item;
}
void buff_pool_release_buff(BuffPoolWapper_t* buffpoolWapper, uint8_t* buff_item){
    // compression_status("buff_pool_release_buff buff[%p].", buff_item);
    buffpoolWapper->buff_pool->release_buff(buff_item);
}

#endif //__BUFFPOOL_CPP__
